#include "jinputcontext.h"
#include "qtevirtualkeyboardglobal.h"
#include "keyboard/jinputcontextwidget.h"

#include <QTECore/QTEGlobal>
#include <QGuiApplication>
#include <QMouseEvent>
#include <QScreen>
#include <QTimer>

namespace QTE
{

class Q_DECL_HIDDEN JInputContextPrivate : public QObject
{
public:
	explicit JInputContextPrivate(JInputContext *q_ptr);
	~JInputContextPrivate();

public:
	void focusObjectPositionSettings();
	void focusObjectPositionRelease();

public:
	bool processShowEvent(QObject *obj, QShowEvent *event);
	bool processHideEvent(QObject *obj, QHideEvent *event);
	bool processCloseEvent(QObject *obj, QCloseEvent *event);
	bool processMoveEvent(QObject *obj, QMoveEvent *event);
	bool processMousePressEvent(QObject *obj, QMouseEvent *event);

public: //slots
	void on_destroyed();

private: //slots
	void on_input(QKeyEvent *event);

private:
	bool processHideCloseEvent(QObject *obj);

public:
	QObject *m_focusObject = nullptr;
	bool m_inputFlag = false;

private:
	QWidget *m_rootWidget = nullptr;
	QPoint m_offset;

	QSize m_oldMaxSize;
	QSize m_oldSize;
};

inline JInputContextPrivate::JInputContextPrivate(JInputContext *q_ptr):
	QObject(q_ptr)
{
	connect(&JInputContextWidget::instance(), &JInputContextWidget::input,
			this, &JInputContextPrivate::on_input);

	connect(&JInputContextWidget::instance(), &JInputContextWidget::panelChanged,
			this, [this](){ m_inputFlag = false; });

	connect(&JInputContextWidget::instance(), &JInputContextWidget::autoPredicts, this, [this](){
		JInputContextWidget::instance().predicts(m_focusObject);
	});
}

JInputContextPrivate::~JInputContextPrivate()
{

}

void JInputContextPrivate::focusObjectPositionSettings()
{
	auto widget = J_OCT(QWidget*, m_focusObject);
	if( widget == nullptr )
		return ;

	m_oldMaxSize = widget->maximumSize();
	m_oldSize = widget->size();

	auto pos = widget->mapToGlobal(QPoint(0,0));
	m_rootWidget = widget;

	while( m_rootWidget->parentWidget() )
		m_rootWidget = m_rootWidget->parentWidget();

	int dWidget = 0;
	for(auto &screen : qApp->screens())
		dWidget += screen->size().width();

	if( pos.x() < 0 )
	{
		m_offset.setX(-pos.x());
		m_rootWidget->move(0, m_rootWidget->y());
		pos.rx() += m_offset.x();
	}

	else if( pos.x() > 0 and pos.x() + widget->width() > dWidget )
	{
		m_offset.setX(dWidget - pos.x() - widget->width());
		m_rootWidget->move(m_rootWidget->x() + m_offset.x(), m_rootWidget->y());
		pos.rx() += m_offset.x();
	}

	if( pos.x() + widget->width() > dWidget )
		widget->setMaximumWidth(dWidget);

	int bottom = JInputContextWidget::instance().y();

	if( pos.y() < 0 )
	{
		m_offset.setY(-pos.y());
		m_rootWidget->move(m_rootWidget->x(), 0);
		pos.ry() += m_offset.y();
	}

	else if( pos.y() > 0 and pos.y() + widget->height() > bottom )
	{
		m_offset.setY(bottom - pos.y() - widget->height());
		m_rootWidget->move(m_rootWidget->x(), m_rootWidget->y() + m_offset.y());
		pos.ry() += m_offset.y();
	}

	if( pos.y() + widget->height() > bottom )
		widget->setMaximumHeight(bottom);
}

void JInputContextPrivate::focusObjectPositionRelease()
{
	if( m_rootWidget == nullptr )
		return ;

	m_rootWidget->move(m_rootWidget->pos() - m_offset);
	auto widget = J_RCT(QWidget*, m_focusObject);

	widget->setMaximumSize(m_oldMaxSize);
	widget->resize(m_oldSize);

	m_rootWidget = nullptr;
}

bool JInputContextPrivate::processShowEvent(QObject *obj, QShowEvent *event)
{
	if( event == nullptr )
		return false;

	auto &widget = JInputContextWidget::instance();
	if( obj == &widget )
	{
		focusObjectPositionSettings();
		return false;
	}
	return false;
}

bool JInputContextPrivate::processHideEvent(QObject *obj, QHideEvent *event)
{
	if( event )
		return processHideCloseEvent(obj);
	return false;
}

bool JInputContextPrivate::processCloseEvent(QObject *obj, QCloseEvent *event)
{
	if( event )
		return processHideCloseEvent(obj);
	return false;
}

bool JInputContextPrivate::processMoveEvent(QObject *obj, QMoveEvent *event)
{
	auto widget = &JInputContextWidget::instance();
	if( event and obj == widget and widget->isVisible() )
	{
		focusObjectPositionRelease();
		focusObjectPositionSettings();
	}
	return false;
}

bool JInputContextPrivate::processMousePressEvent(QObject *obj, QMouseEvent *event)
{
	if( event == nullptr or obj == m_focusObject )
		return false;

	auto widget = qobject_cast<QWidget*>(obj);
	if( widget == nullptr )
		return false;

	auto &ICWidget = JInputContextWidget::instance();
	if( ICWidget.isHidden() )
		return false;

	auto pos = widget->mapToGlobal(event->pos());
	int x = ICWidget.x();
	int y = ICWidget.y();

	if( pos.x() < x or pos.x() > x + ICWidget.width() or
		pos.y() < y or pos.y() > y + ICWidget.height() )
	{
		ICWidget.hide();
		return true;
	}
	return false;
}

inline void JInputContextPrivate::on_destroyed()
{
	JInputContextWidget::instance().hide();
}

void JInputContextPrivate::on_input(QKeyEvent *event)
{
	if( m_focusObject == nullptr )
		return ;

	m_inputFlag = true;
	qApp->postEvent(m_focusObject, event);

	QTimer::singleShot(10, this, [this](){
		JInputContextWidget::instance().predicts(m_focusObject);
	});
}

bool JInputContextPrivate::processHideCloseEvent(QObject *obj)
{
	if( obj == &JInputContextWidget::instance() )
	{
		focusObjectPositionRelease();
		static QWidget w; //巨坑：在 EGLFS 平台下，可能会出现窗口无法响应鼠标（第一次触摸）事件

		QTimer::singleShot(1, [](){ //先找一个爸爸，在下次显示前再把爸爸扔了就解决了
			JInputContextWidget::instance().setParent(&w);
		});
	}
	return false;
}

/*----------------------------------------------------------------------------*/

JInputContext::JInputContext() :
	d_ptr(new JInputContextPrivate(this))
{
	qApp->installEventFilter(this);
}

JInputContext::~JInputContext()
{

}

//retur true if plugin is enabled
bool JInputContext::isValid() const
{
	return true;
}

//this value will be available in QGuiApplication::inputMethod()->keyboardRectangle()
QRectF JInputContext::keyboardRect() const
{
	return JInputContextWidget::instance().rect();
}

//this value will be available in QGuiApplication::inputMethod()->isVisible()
bool JInputContext::isInputPanelVisible() const
{
	return JInputContextWidget::instance().isVisible();
}

//editor pointer
void JInputContext::setFocusObject(QObject *object)
{
	if( not needInputMethod(object) )
		return ;

	if( d_ptr->m_focusObject )
		d_ptr->m_focusObject->disconnect(d_ptr);

	d_ptr->m_focusObject = object;
	connect(object, &QObject::destroyed, d_ptr, &JInputContextPrivate::on_destroyed);

	auto widget = qobject_cast<QWidget*>(object);
	if( widget and widget->inputMethodHints() & Qt::ImhFormattedNumbersOnly )
		JInputContextWidget::instance().setNumberKeyboardLock(true);
	else
		JInputContextWidget::instance().setNumberKeyboardLock(false);
}

void JInputContext::showInputPanel()
{
	QPlatformInputContext::showInputPanel();
	auto &widget = JInputContextWidget::instance();

	if( widget.isVisible() )
		return ;

	else if( d_ptr->m_focusObject and needInputMethod(d_ptr->m_focusObject) )
	{
		widget.predicts(d_ptr->m_focusObject);
		widget.setParent(nullptr);
		widget.show();
	}
}

void JInputContext::hideInputPanel()
{
	QPlatformInputContext::hideInputPanel();
	if( d_ptr->m_inputFlag )
	{
		JInputContextWidget::instance().hide();
		d_ptr->m_inputFlag = false;
	}
}

bool JInputContext::eventFilter(QObject *watched, QEvent *event)
{
	switch( event->type() )
	{
		case QEvent::Show:
			return d_ptr->processShowEvent(watched, J_DCT(QShowEvent*, event));

		case QEvent::Hide:
			return d_ptr->processHideEvent(watched, J_DCT(QHideEvent*, event));

		case QEvent::Close:
			return d_ptr->processCloseEvent(watched, J_DCT(QCloseEvent*, event));

		case QEvent::Move:
			return d_ptr->processMoveEvent(watched, J_DCT(QMoveEvent*, event));

		case QEvent::MouseButtonPress:
			return d_ptr->processMousePressEvent(watched, J_DCT(QMouseEvent*, event));

		default: break;
	}
	return false;
}

} //namespace QTE
